﻿using diary.POCO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Threading.Tasks;
using NLog;
using System.IO;

namespace diary.DAL
{
    public class DiarySaver
    {
        private Logger logger = LogManager.GetCurrentClassLogger();

        public bool SaveDiary(Diary diary)
        {
            bool successfulSave = false;
            XmlDocument docDiary = new XmlDocument();
            try
            {
                // XML HEAD
                logger.Log(LogLevel.Info, "");
                logger.Log(LogLevel.Info, "Creating new XML document");
                XmlDeclaration dec = docDiary.CreateXmlDeclaration("1.0", "utf-8", null);
                docDiary.AppendChild(dec);
                XmlElement root = docDiary.CreateElement("diary");
                root.SetAttribute("version", "0.1");
                docDiary.AppendChild(root);
                logger.Log(LogLevel.Info, "Creating section all-time best");
                root.AppendChild(CreateBestElement(docDiary, diary.Best));
                logger.Log(LogLevel.Info, "Creating section years");
                root.AppendChild(CreateYears(docDiary, diary.Years));

                // TODO: Backup!
                logger.Log(LogLevel.Info, "Managing Backups");
                ManageBackups(docDiary, @"C:\TempFiles\Backups", 10, 10);

                // SAVE THE XML
                logger.Log(LogLevel.Info, "Saving " + Constants.FILE_DIARY);
                docDiary.Save(Constants.FILE_DIARY);
            }
            catch (Exception ex)
            {
                logger.Log(LogLevel.Error, "Unable to create diary xml document : " + ex.Message);
                logger.Log(LogLevel.Error, ex.StackTrace);
                throw ex;
            }

            return successfulSave;
        }

        private XmlElement CreateYears(XmlDocument docDiary, List<Year> years)
        {
            XmlElement elementDiaryYears = docDiary.CreateElement("years");
            foreach(Year year in years)
            {
                logger.Log(LogLevel.Info, "Creating section year " + year.CurrentYear);
                XmlElement elementDiaryYearsYear = docDiary.CreateElement("year");
                elementDiaryYearsYear.SetAttribute("value", year.CurrentYear.ToString());
                elementDiaryYears.AppendChild(elementDiaryYearsYear);
                elementDiaryYearsYear.AppendChild(CreateBestElement(docDiary, year.Best));
                elementDiaryYearsYear.AppendChild(CreateMonths(docDiary, year.Months));
            }
            return elementDiaryYears;
        }

        private XmlElement CreateMonths(XmlDocument docDiary, List<Month> months)
        {
            XmlElement elementDiaryMonths = docDiary.CreateElement("months");
            foreach(Month month in months)
            {
                logger.Log(LogLevel.Info, "Creating section month " + month.CurrentMonth);
                XmlElement elementDiaryMonthsMonth = docDiary.CreateElement("month");
                elementDiaryMonthsMonth.SetAttribute("value", month.CurrentMonth.ToString());
                elementDiaryMonths.AppendChild(elementDiaryMonthsMonth);
                elementDiaryMonthsMonth.AppendChild(CreateBestElement(docDiary, month.Best));
                elementDiaryMonthsMonth.AppendChild(CreateDays(docDiary, month.Days));
            }
            return elementDiaryMonths;
        }

        private XmlElement CreateDays(XmlDocument docDiary, List<Day> days)
        {
            XmlElement elementDiaryDays = docDiary.CreateElement("days");
            foreach (Day day in days)
            {
                elementDiaryDays.AppendChild(CreateDay(docDiary, day));
            }
            return elementDiaryDays;
        }

        private XmlElement CreateBestElement(XmlDocument docDiary, Best best)
        {
            XmlElement elementDiaryBest = docDiary.CreateElement("best");

            // BOOKS
            logger.Log(LogLevel.Info, "Creating section best books");
            elementDiaryBest.AppendChild(CreateBooks(docDiary, best.Books));

            // SONGS
            logger.Log(LogLevel.Info, "Creating section best songs");
            elementDiaryBest.AppendChild(CreateSongs(docDiary, best.Songs));

            // ALBUMS
            logger.Log(LogLevel.Info, "Creating section best albums");
            elementDiaryBest.AppendChild(CreateAlbums(docDiary, best.Albums));

            // TELEVISION
            logger.Log(LogLevel.Info, "Creating section best television");
            elementDiaryBest.AppendChild(CreateTelevision(docDiary, best.Television));

            // MOVIES
            logger.Log(LogLevel.Info, "Creating section best movies");
            elementDiaryBest.AppendChild(CreateMovies(docDiary, best.Movies));

            // EVENTS
            logger.Log(LogLevel.Info, "Creating section best events");
            elementDiaryBest.AppendChild(CreateEvents(docDiary, best.Events));

            // PICTURES
            logger.Log(LogLevel.Info, "Creating section best pictures");
            elementDiaryBest.AppendChild(CreatePictures(docDiary, best.Pictures));

            return elementDiaryBest;
        }

        private XmlElement CreateBooks(XmlDocument docDiary, List<Book> books)
        {
            XmlElement elementBestBooks = docDiary.CreateElement("books");
            foreach(Book book in books)
            {
                elementBestBooks.AppendChild(CreateBook(docDiary, book));
            }
            return elementBestBooks;
        }

        private XmlElement CreateEvents(XmlDocument docDiary, List<Event> events)
        {
            XmlElement elementEvents = docDiary.CreateElement("events");
            foreach (Event devent in events)
            {
                elementEvents.AppendChild(CreateEvent(docDiary, devent));
            }
            return elementEvents;
        }

        private XmlElement CreateAlbums(XmlDocument docDiary, List<Album> albums)
        {
            XmlElement elementBestAlbums = docDiary.CreateElement("albums");
            foreach(Album album in albums)
            {
                elementBestAlbums.AppendChild(CreateAlbum(docDiary, album));
            }
            return elementBestAlbums;
        }

        private XmlElement CreateTelevision(XmlDocument docDiary, List<Television> tvshows)
        {
            XmlElement elementBestTV = docDiary.CreateElement("television");
            foreach(Television tvshow in tvshows)
            {
                elementBestTV.AppendChild(CreateTVShow(docDiary, tvshow));
            }

            return elementBestTV;
        }

        private XmlElement CreateMovies(XmlDocument docDiary, List<Movie> movies)
        {
            XmlElement elementBestMovies = docDiary.CreateElement("movies");
            foreach(Movie movie in movies)
            {
                elementBestMovies.AppendChild(CreateMovie(docDiary, movie));
            }
            
            return elementBestMovies;
        }

        private XmlElement CreateWorkouts(XmlDocument docDiary, List<Workout> workouts)
        {
            XmlElement elementWorkouts = docDiary.CreateElement("workouts");
            foreach (Workout workout in workouts)
            {
                elementWorkouts.AppendChild(CreateWorkout(docDiary, workout));
            }

            return elementWorkouts;
        }

        private XmlElement CreatePictures(XmlDocument docDiary, List<Picture> pics)
        {
            XmlElement elementPics = docDiary.CreateElement("pictures");
            foreach (Picture pic in pics)
            {
                elementPics.AppendChild(CreatePicture(docDiary, pic));
            }

            return elementPics;
        }

        private XmlElement CreateSongs(XmlDocument docDiary, List<Song> songs)
        {
            XmlElement elementBestSongs = docDiary.CreateElement("songs");
            foreach (Song song in songs)
            {
                elementBestSongs.AppendChild(CreateSong(docDiary, song));
            }
            return elementBestSongs;
        } 

        private XmlElement CreateSong(XmlDocument docDiary, Song song)
        {
            XmlElement elementSong = docDiary.CreateElement("song");
            elementSong.SetAttribute("rank", song.Rank.ToString());
            elementSong.SetAttribute("artist", song.Artist);
            elementSong.SetAttribute("title", song.Title);
            return elementSong;
        }

        private XmlElement CreatePicture(XmlDocument docDiary, Picture pic)
        {
            XmlElement elementPic = docDiary.CreateElement("picture");
            elementPic.SetAttribute("rank", pic.Rank.ToString());
            elementPic.SetAttribute("value", pic.Value.ToString());
            elementPic.InnerText = pic.File;
            return elementPic;
        }

        private XmlElement CreateEvent(XmlDocument docDiary, Event devent)
        {
            XmlElement elementEvent = docDiary.CreateElement("event");
            elementEvent.SetAttribute("rank", devent.Rank.ToString());
            elementEvent.SetAttribute("magnitude", devent.Magnitude.ToString());
            elementEvent.InnerText = devent.Content;
            return elementEvent;
        }

        private XmlElement CreateMovie(XmlDocument docDiary, Movie movie)
        {
            XmlElement element = docDiary.CreateElement("movie");
            element.SetAttribute("rank", movie.Rank.ToString());
            element.SetAttribute("title", movie.Title);
            element.SetAttribute("year", movie.Year.ToString());
            return element;
        }

        private XmlElement CreateWorkout(XmlDocument docDiary, Workout workout)
        {
            XmlElement element = docDiary.CreateElement("workout");
            element.SetAttribute("title", workout.Title);
            return element;
        }

        private XmlElement CreateBook(XmlDocument docDiary, Book book)
        {
            XmlElement element = docDiary.CreateElement("book");
            element.SetAttribute("rank", book.Rank.ToString());
            element.SetAttribute("title", book.Title);
            element.SetAttribute("author", book.Author);
            return element;
        }

        private XmlElement CreateAlbum(XmlDocument docDiary, Album album)
        {
            XmlElement element = docDiary.CreateElement("album");
            element.SetAttribute("rank", album.Rank.ToString());
            element.SetAttribute("artist", album.Artist);
            element.SetAttribute("title", album.Title);
            element.SetAttribute("year", album.Year);
            return element;
        }

        private XmlElement CreateTVShow(XmlDocument docDiary, Television tvshow)
        {
            XmlElement element = docDiary.CreateElement("tvshow");
            element.SetAttribute("rank", tvshow.Rank.ToString());
            element.SetAttribute("title", tvshow.Title);
            return element;
        }

        private XmlElement CreateDay(XmlDocument docDiary, Day day)
        {
            XmlElement element = docDiary.CreateElement("day");

            // ATTRIBUTES
            element.SetAttribute("value", day.CurrentDay.ToString());
            element.SetAttribute("weekday", day.CurrentDate.ToString("ddd"));

            // ENTRY
            XmlElement elementEntry = docDiary.CreateElement("entry");
            elementEntry.InnerText = day.Entry.Content;
            element.AppendChild(elementEntry);

            // BOOKS
            element.AppendChild(CreateBooks(docDiary, day.Books));

            // MOVIES
            element.AppendChild(CreateMovies(docDiary, day.Movies));

            // EVENTS
            element.AppendChild(CreateEvents(docDiary, day.Events));

            // PICS
            element.AppendChild(CreatePictures(docDiary, day.Pictures));

            // TELEVISION
            element.AppendChild(CreateTelevision(docDiary, day.Television));

            // WORKOUTS
            element.AppendChild(CreateWorkouts(docDiary, day.Workouts));

            // WORK
            XmlElement elementWork = docDiary.CreateElement("work");
            elementWork.InnerText = day.Work.Content;
            element.AppendChild(elementWork);

            // ALBUMS
            element.AppendChild(CreateAlbums(docDiary, day.Albums));

            return element;
        }

        private void ManageBackups(XmlDocument doc, string backupFolder, int MaxPerDay, int MaxDayOld)
        {
            doc.Save(Path.Combine(backupFolder, "BACKUP_" + DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".xml"));
            FileInfo[] files = new DirectoryInfo(backupFolder).GetFiles();
            IDictionary<string, IList<FileInfo>> fileDic = new Dictionary<string, IList<FileInfo>>();
            foreach (FileInfo file in files)
            {
                if ((DateTime.Now - file.LastWriteTime).Days < MaxDayOld)
                {
                    string registerDate = file.LastWriteTime.ToString("yyyyMMdd");
                    if (fileDic.ContainsKey(registerDate) == false)
                    {
                        fileDic[registerDate] = new List<FileInfo>();
                    }
                    fileDic[registerDate].Add(file);
                }
                else // TOO OLD
                {
                    File.Delete(file.FullName);
                }
            }

            ICollection<string> datekeys = fileDic.Keys;
            foreach (string key in datekeys)
            {
                // Is list over the maximum?
                if (fileDic[key].Count > MaxPerDay)
                {
                    List<FileInfo> orderedList = fileDic[key].OrderBy(x => x.LastWriteTime).ToList();
                    while (orderedList.Count > MaxPerDay)
                    {
                        File.Delete(orderedList[0].FullName);
                        orderedList.RemoveAt(0);
                    }
                }

            }
        }
    }
}
